﻿using System;
using smp.ButterflyCakes.Domain.Interfaces;
using smp.ButterflyCakes.Models;
using smp.ButterflyCakes.Models.Data;
using smp.ButterflyCakes.Models.FormModels.Order;
using smp.ButterflyCakes.Models.InputModels.Order;
using smp.ButterflyCakes.Repository.Implementation;
using smp.ButterflyCakes.Repository.Interfaces;

namespace smp.ButterflyCakes.Domain.Implementation
{
    public class Cake : ICake
    {
        #region members and constructors

        private readonly IDomainCakeRepository _repository;
        private readonly NextOrderStage _currentLastStage;
        private DomainResponse _response;
        private readonly bool _loadSuccess;
        private readonly ComponentType _type;
        private readonly Guid _id;
        private readonly Guid _orderId;
        private readonly int _numberOfFlavours;
        private readonly int _actualNumberOfFlavours;

        public Cake()
            : this(new DomainCakeRepository())
        {
        }

        public Cake(IDomainCakeRepository repository)
        {
            _repository = repository;
        }

        public Cake(Guid id)
            : this(id, new DomainCakeRepository())
        {
        }

        public Cake(Guid id, IDomainCakeRepository repository)
        {
            _repository = repository;
            var data = _repository.GetCakeData(id);
            if (data == null || data.LastStage == null)
            {
                _loadSuccess = false;
                _response = new DomainResponse("Failed to load order cake for Id : " + id);
                return;
            }
            _currentLastStage = data.LastStage;
            _type = data.Type;
            _id = data.Id;
            _orderId = data.OrderId;
            _numberOfFlavours = data.NumberOfFlavours;
            _actualNumberOfFlavours = data.ActualNumberOfFlavours;
            _loadSuccess = true;
        }

        #endregion

        #region Implementation of ICake

        public DomainResponse SetCakeType(CakeTitleModel model)
        {
            if (!_loadSuccess)
                return _response;

            if (!model.IsCarved)
            {
                model.NumberOfFlavours = 1;
            }

            StageDataItem stage = null;
            if (_currentLastStage.Stage == Stage.ComponentTitle)
            {
                stage = new StageDataItem(_id, GetNextStageTypeForcakeType(model), _currentLastStage);
                _response = new DomainResponse(stage.Stage);
            }
            else
            {
                _response = new DomainResponse(_currentLastStage.Stage);
            }

            if (!_repository.SaveCakeTitle(model, stage))
                return new DomainResponse("Failed to save cake title");
            return _response;
        }

        private Stage GetNextStageTypeForcakeType(CakeTitleModel model)
        {
            switch (model.Type)
            {
                case ComponentType.CustomCake:
                    return model.IsCarved ? Stage.ComponentHeight : Stage.ComponentFlavour;
                case ComponentType.CustomCupCakes:
                case ComponentType.CustomGiantCupCake:
                case ComponentType.Custom3DCake:
                    return Stage.ComponentFlavour;
                case ComponentType.RepetoireCake:
                    break;
                case ComponentType.RepetoireCupCakes:
                    break;
                case ComponentType.RepetoireGiantCupCake:
                    break;
                case ComponentType.Repetoire3DCake:
                    break;
            }
            throw new Exception("Invalid cake type : " + model.Type);
        }

        public DomainResponse SetCakeHeightAndNumber(ComponentHeightModel model)
        {
            if (!_loadSuccess)
                return _response;

            if(model.HeightType.HasValue && model.HeightType.Value == CakeHeightType.StripedCake)
            {
                if(!model.NumberOfCakes.HasValue || model.NumberOfCakes.Value == 0)
                {
                    return new DomainResponse("Please enter a valid number of cakes for striping.");
                }
                model.NumberOfFlavours = model.NumberOfCakes;
            }
            else
            {
                model.NumberOfFlavours = 1;
            }

            StageDataItem nextStage = null;
            if(_currentLastStage.Stage == Stage.ComponentHeight)
            {
                nextStage = new StageDataItem(_id, Stage.ComponentFlavour, _currentLastStage);
                _response = new DomainResponse(nextStage.Stage);
            }
            else
            {
                _response = new DomainResponse(_currentLastStage.Stage);
            }

            if(!_repository.SaveCakeHeight(model, nextStage))
                return new DomainResponse("Failed to save cake height and number inforamtion");

            return _response;
        }

        public DomainResponse AddFlavour(AddFlavourInput model)
        {
            if(!_loadSuccess)
                return _response;

            if(_actualNumberOfFlavours + 1 > _numberOfFlavours)
                return new DomainResponse("Cannot add flavour, current cake configuration only supports " +
                                       _numberOfFlavours + " flavours.");

            if(!_repository.AddCakeFlavour(model))
                return new DomainResponse("Failed to save flavour");

            return new DomainResponse(Stage.ComponentFlavour);
        }

        public DomainResponse RemoveFlavour(RemoveFlavourInput model)
        {
            if(!_loadSuccess)
                return _response;

            if(!_repository.RemoveCakeFlavour(model))
                return new DomainResponse("Failed to remove flavour");

            return new DomainResponse(Stage.ComponentFlavour);
        }

        public DomainResponse SetCakeFlavour(ComponentFlavourModel model)
        {
            if (!_loadSuccess)
                return _response;

            StageDataItem nextStage = null;
            if(_currentLastStage.Stage == Stage.ComponentFlavour)
            {
                switch (_type)
                {
                    case ComponentType.CustomCake:
                        nextStage = new StageDataItem(_id, Stage.ComponentSize, _currentLastStage);
                        break;
                    case ComponentType.RepetoireCake:
                        //todo check, if size if optional
                        throw new NotImplementedException();
                        break;
                    case ComponentType.CustomCupCakes:
                    case ComponentType.CustomGiantCupCake:
                    case ComponentType.Custom3DCake:
                    case ComponentType.RepetoireCupCakes:
                    case ComponentType.RepetoireGiantCupCake:
                    case ComponentType.Repetoire3DCake:
                        nextStage = new StageDataItem(_id, Stage.ComponentQuantity, _currentLastStage);
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
                _response = new DomainResponse(nextStage.Stage);
            }
            else
            {
                _response = new DomainResponse(_currentLastStage.Stage);
            }

            if(!_repository.SaveCakeFlavour(model, nextStage))
                return new DomainResponse("Failed to save flavour");
            return _response;
        }

        public DomainResponse SaveCakeSize(SaveSizeInput model)
        {
            if(!_loadSuccess)
                return _response;
            
            if(!_repository.SaveCakeSize(model))
                return new DomainResponse("Failed to save size");

            return new DomainResponse(Stage.ComponentSize);
        }

        public DomainResponse RemoveCakeSize(RemoveSizeInput model)
        {
            if (!_loadSuccess)
                return _response;

            if (!_repository.RemoveCakeSize(model))
                return new DomainResponse("Failed to save size");

            return new DomainResponse(Stage.ComponentSize);
        }

        public DomainResponse SetCakeQuantity(ComponentQuantityModel model)
        {
            if (!_loadSuccess)
                return _response;

            if(_currentLastStage.Stage == Stage.ComponentQuantity)
            {
                var stage = new StageDataItem(_id, Stage.ComponentFinishing, _currentLastStage);
                _response = new DomainResponse(stage.Stage);
            }
            else
            {
                _response = new DomainResponse(_currentLastStage.Stage);
            }

            if (!_repository.SaveCakeQuantity(model, null))
            {
                return new DomainResponse("Failed to save quantity");
            }
            return _response;
        }

        public DomainResponse SetCakeSize(ComponentSizeModel model)
        {
            if (!_loadSuccess)
                return _response;

            StageDataItem nextStage = null;
            if (_currentLastStage.Stage == Stage.ComponentSize)
            {
                nextStage = new StageDataItem(_id, Stage.ComponentButterCream, _currentLastStage);
                _response = new DomainResponse(nextStage.Stage);
            }
            else
            {
                _response = new DomainResponse(_currentLastStage.Stage);
            }

            if (!_repository.SetCakeSize(model, nextStage))
                return new DomainResponse("Failed to save cake size");
            return _response;
        }
        
        public DomainResponse SetButterCream(ComponentButterCreamModel model)
        {
            if(!_loadSuccess)
                return _response;
            
            StageDataItem stage = null;
            if(_currentLastStage.Stage == Stage.ComponentButterCream)
            {
                stage = new StageDataItem(_orderId, _id, Stage.ComponentPreserve, _currentLastStage.Id, _currentLastStage.Index + 1);
                _response = new DomainResponse(stage.Stage);
            }
            else
            {
                _response = new DomainResponse(_currentLastStage.Stage);
            }

            if(!_repository.SaveCakeButtercream(model, stage))
                return new DomainResponse("Failed to save filling data");
            
            return _response;
        }

        public DomainResponse SetPreserve(ComponentPreserveModel model)
        {
            if (!_loadSuccess)
                return _response;

            StageDataItem stage = null;
            if (_currentLastStage.Stage == Stage.ComponentPreserve)
            {
                stage = new StageDataItem(_id, Stage.ComponentFinishing, _currentLastStage);
                _response = new DomainResponse(stage.Stage);
            }
            else
            {
                _response = new DomainResponse(_currentLastStage.Stage);
            }

            if (!_repository.SaveCakePreserve(model, stage))
                return new DomainResponse("Failed to save preserve data");

            return _response;
        }

        public DomainResponse SetFinish(ComponentFinishingModel model)
        {
            if(!_loadSuccess)
                return _response;

            StageDataItem stage = null;
            if(_currentLastStage.Stage == Stage.ComponentFinishing)
            {
                stage = new StageDataItem(_id, Stage.ComponentDecoration, _currentLastStage);
                _response = new DomainResponse(stage.Stage);
            }
            else
            {
                _response = new DomainResponse(_currentLastStage.Stage);
            }

            if(!_repository.SaveFinish(model, stage))
                return new DomainResponse("Failed to save finishing data");
            
            return _response;
        }

        #endregion
    }
}

